﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Collections;
using System.Net.Sockets;
using System.Threading;
using System.Net;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
using System.ComponentModel;
using System.Diagnostics;
using System.Text.RegularExpressions;


/*COMMENTED NETWORK CODE:
private void cbConnectToServer_CheckStateChanged(object sender, EventArgs e)
{
    try
    {
        if (cbConnectToServer.Checked)
        {
            tscbPlayerName.Enabled= true;
            nudClientToServerPort.Enabled = false;

            playerName = tscbPlayerName.Text;
            string host = "localhost";
            IPAddress ServerIP;
            if (tbServerIP.Text.Trim().ToLower() == "localhost")
            {
                host = "127.0.0.1";
                ServerIP = IPAddress.Parse(host);
                //ServerIP. = AddressFamily.InterNetwork;
            }
            else
            {
                host = tbServerIP.Text;
                ServerIP = IPAddress.Parse(host);
                //ServerIP.AddressFamily = AddressFamily.Unknown;
            }


            int clientToServerPort;
                
            client = new TcpClient();
            clientToServerPort = (int)(nudClientToServerPort.Value);

            IPEndPoint serverEndPoint = new IPEndPoint(ServerIP, clientToServerPort);
            try
            {
                client.Connect(serverEndPoint);
            }
            catch (Exception eConnect)
            {
                InfoLogIt("Cannot connect to server. ", eConnect, "Cannot connect to remote server.");//old NOTE: This could be due to a blocked port in the firewall. Disable the firewall and test if this solves your problem. If this is not possible just make an exception for my program, and be sure to allow public network traffic or your friends cannot connect unless they are on the same private network as you are.");
                cbConnectToServer.Checked = false;
                return;
            }
            //tmrClientSend.Enabled = true; //!tmrClientSend.Enabled;
            tmrServerQueueHandler.Enabled = true;
            tmrTargetDamageFromLog.Enabled = true;
            //clientStream.Close();
            tscbPlayerName.Enabled = true;

            //COMMENTED NETWORK CODE: clientThread = new Thread(new ThreadStart(ClientCommunication));
            //COMMENTED NETWORK CODE: clientThread.Start();
        }
       else  {
            tscbPlayerName.Enabled = true;
            nudClientToServerPort.Enabled = true;
                    
            //tmrClientSend.Enabled = false;
            clientThread.Abort();
            tmrServerQueueHandler.Enabled = false;
            tmrTargetDamageFromLog.Enabled = false;
            client.Close();//prevent changing name during battle and require to login if changing name
            client = null;
            clientStream = null; 
        }

    }
    catch (Exception IPFormatException)
    {
        InfoLogIt("Server IP address format exception. Please review the server IP address you gave in the Server address field under the Client tab.\n", IPFormatException, "Invalid destination or server I P address.");
        cbConnectToServer.Checked = false;
        return;
    }
}      */


/*COMMENTED NETWORK CODE:
private void tmrDamageDealersFromLog(object sender, EventArgs e)
{
    Hashtable targets = GetAllTargetsRaidsTargetting();

    if (targets !=null)
    foreach (string target in targets.Keys)
    {
        TreeNode targetNode;
        if (tvRaidTargets.Nodes.ContainsKey(target))
            targetNode = tvRaidTargets.Nodes[target];
        else
            targetNode = tvRaidTargets.Nodes.Add(target, target); //should use a list but this Hashtable with duplicate key/value is fine for now

        long totalTargetDamage = 0;

        TreeNode lastNodeAdded = null;

        List<NameTargetLongAmount> clientDamageOnTarget = new List<NameTargetLongAmount>();
        foreach (string clientDamageDealer in htDamageDealers.Keys)
        {
            Hashtable theTable = (Hashtable)htDamageDealers[clientDamageDealer];

            string sDamageDone = theTable[target].ToString();
            long iDamageDone = long.Parse(sDamageDone);
            totalTargetDamage += iDamageDone;
            clientDamageOnTarget.Add(new NameTargetLongAmount(clientDamageDealer, iDamageDone));
        }

        clientDamageOnTarget.Sort();

        foreach (NameTargetLongAmount clientDamage in clientDamageOnTarget)
        {
            string cli = clientDamage.Target;
            string sText = clientDamage.DamageOrHealing + "=>" + cli;
            float percentThreatOfTotal = (float)clientDamage.DamageOrHealing / totalTargetDamage * 100;
            string sPercentThreatOfTotal = string.Format("{0:0.0}", percentThreatOfTotal);
            sText += "(" + sPercentThreatOfTotal + "%)";
            if (targetNode.Nodes.ContainsKey(cli))
                targetNode.Nodes[cli].Text = sText;
            else
                targetNode.Nodes.Add(cli, sText);
        }
        //add total 
        string sTotalLine = totalTargetDamage.ToString() + " ^^^(100%)";
        TreeNode sTotalNodeForTarget = null;
        if (targetNode.Nodes.ContainsKey("Target Total Damage"))
        {
            sTotalNodeForTarget = targetNode.Nodes["Target Total Damage"];
            sTotalNodeForTarget.Text = sTotalLine;
        }
        else
        {
            sTotalNodeForTarget=targetNode.Nodes.Add("Target Total Damage", sTotalLine);
        }
        sTotalNodeForTarget.NodeFont = subTotalFont;


        if (lastNodeAdded != null && lastNodeAdded.Parent.GetNodeCount(false) > 1) //dont need to show a total if only one entry
        {
            string sSubTotal = totalTargetDamage.ToString() + " (Total)";
            if (targetNode.Nodes.ContainsKey("Total"))
                targetNode.Nodes["Total"].Text = sSubTotal;
            else
            {
                TreeNode tnNew = targetNode.Nodes.Add("Total", "Total: " + sSubTotal);
                tnNew.NodeFont = subTotalFont;
            }
        }

        if (htDamageDealers.Count > 0)
        {
            tvRaidTargets.Refresh();
        }

    }

    //until i can figure out how to sort the damage dealers under the target node, a simple sort will do
     tvRaidTargets.Sort();

    ////////////////////
    //////////////////// DO THREAT LIST IN TREE VIEW
    ////////////////////

    if (cbServerRunning.Checked && mServer != null)
    {
        Hashtable targetsHateOnPlayers_OfHash = mServer.targetsHateOnPlayers_OfHash;
        foreach(string tar in targetsHateOnPlayers_OfHash.Keys)
        {
            TreeNode targetNode = null;
            if (tvThreat.Nodes.ContainsKey(tar))
                targetNode = tvThreat.Nodes[tar];
            else
            {
                targetNode = tvThreat.Nodes.Add(tar, tar);
            }

            Hashtable targetsHateOnPlayers = (Hashtable)targetsHateOnPlayers_OfHash[tar];

            float targetsHate = 0;
            //float totalTargetsHate = 0;

            foreach (string player in htClientPlayers.Keys)
            {
                if (targetNode.Nodes.ContainsKey(player))
                {
                    if (targetsHateOnPlayers.ContainsKey(player))
                    {
                        targetsHate = (float)targetsHateOnPlayers[player];
                        targetNode.Nodes[player].Text = player + "->" + targetsHate.ToString();
                    }
                }
                else
                {
                    if (targetsHateOnPlayers.ContainsKey(player))
                    {
                        targetsHate = (float)targetsHateOnPlayers[player];
                        targetNode.Nodes.Add(player, player + "->" + targetsHate.ToString());
                    }
                }
            }

        }

        tvThreat.Sort();
    }
}
 
private Hashtable GetAllTargetsRaidsTargetting()
{
    Hashtable targets = new Hashtable(); //of type string, long
    if (htDamageDealers == null)
        return null;
    else
    foreach (string client in htDamageDealers.Keys)
    {
        Hashtable htClient = (Hashtable)htDamageDealers[client];
        foreach (string target in htClient.Keys)
        {
            if (!targets.Contains(target))
                targets.Add(target, 0);
        }
    }
    return targets;
}
*/

//private void tsmiClearSelfDamage(object sender, EventArgs e)
//{
//    htHostileTargets.Clear();
//    tvDamageAndHealing.Nodes[0].Nodes.Clear();
//}

namespace AbyssCombatLogger
{
    class Server
    {
        public delegate bool ClientNameMessage(string playerName);
        public event ClientNameMessage ClientLoggedOn;
        public event ClientNameMessage ClientLoggedOff;
        public delegate void NetworkMessage(Hashtable msg, string clientID);

        public TcpListener tcpListener;
        public Thread listenThread;
        const int def_port = 3000;
        private int port = -1;


        public void CloseEverything()
        {

            if (listenThread != null)
            {
                listenThread.Abort();
                listenThread = null;
            }
            if (tcpListener != null)
            {
                tcpListener.Stop();
                tcpListener = null;
            }
        }

        public Hashtable serverDamageForAllUsers;
        public Hashtable targetsHateOnPlayers_OfHash = new Hashtable();

        public delegate void ServerError(Exception serverEx);
        public ServerError ServerHandleClientCommError;

        public Server(int port, ref Hashtable serverGroupData)
        {
            this.port = port;
            this.ServerDataForAllUsers = serverGroupData;
        }
        public void Start()
        {
            this.tcpListener = new TcpListener(IPAddress.Any, port);
            //string stemp = this.tcpListener.LocalEndpoint.AddressFamily.ToString();
            this.listenThread = new Thread(new ThreadStart(ListenForClients));
            this.listenThread.Start();
        }


        //should think of setting up network with broadcasts
        private void ListenForClients()
        {
          this.tcpListener.Start();

          while (listenThread != null && listenThread.IsAlive && tcpListener != null)
          {
            //blocks until a client has connected to the server
            TcpClient client = this.tcpListener.AcceptTcpClient();

            //create a thread to handle communication 
            //with connected client
            Thread clientThread = new Thread(new ParameterizedThreadStart(HandleClientComm));
            clientThread.Start(client);
          }
        }


        public Hashtable ServerDataForAllUsers
        {
            get { return serverDamageForAllUsers; }
            set { serverDamageForAllUsers = value; }
        }

        //http://social.msdn.microsoft.com/Forums/en-US/netfxbcl/thread/2bc7e006-5218-4abc-a3db-561f141704bd (Accessed: Oct 13, 2010)
        private void RaiseServerCommError(Exception serverException)
        {
            // Get local event for thread safety purposes
            ServerError handler = ServerHandleClientCommError;
            if (handler == null)
                return;
            
            foreach (ServerError singleCast in handler.GetInvocationList())
            {
                ISynchronizeInvoke syncInvoke = singleCast.Target as ISynchronizeInvoke;
                try
                {
                    if (syncInvoke != null && syncInvoke.InvokeRequired)
                    {
                        // Invokie the event on the main thread
                        //syncInvoke.Invoke(handler, new object[] { this, null });
                        syncInvoke.Invoke(handler, new object[] {serverException });
                    }
                    else {
                        // Raise the event
                        singleCast(serverException);
                    }
                }
                catch{}
            }
        }

        private void HandleClientComm(object client)
        {
            Queue<ParsedLogEvent> newHealsFromClient = null;
            Queue<ParsedLogEvent> newDamagesFromClient = null;

            TcpClient tcpClient = (TcpClient)client;
            NetworkStream clientStream = tcpClient.GetStream();
            string clientPlayerName;
            try
            {
                //first get the client name / player tag
                StreamReader sr = new StreamReader(clientStream);
                clientPlayerName = sr.ReadLine();

                if (ClientLoggedOn(clientPlayerName) == false)
                {
                    clientPlayerName = null; //this should break any further action
                }
            }
            catch (Exception ex)
            {
                ServerHandleClientCommError(ex);
                clientPlayerName = null;
                ClientLoggedOff(clientPlayerName);
            }

            while (client != null && tcpClient.Connected && clientStream != null && clientPlayerName != null)
            {
                try
                {
                    //Get data ready for write in bytes
                    //WaitForWriteable(clientStream);

                    WriteTo(clientStream, serverDamageForAllUsers, "Server");
                    newDamagesFromClient = (Queue<ParsedLogEvent>)ReadFrom(clientStream, "Server");
                    newHealsFromClient = (Queue<ParsedLogEvent>)ReadFrom(clientStream, "Server");
                }
                catch (Exception ex1)
                {
                    //a socket error has occured
                    RaiseServerCommError(ex1);
                    ClientLoggedOff(clientPlayerName);
                    break;
                }

                ////////calc.HandleHate(ref this.targetsHateOnPlayers_OfHash, newHealsFromClient, newDamagesFromClient, clientPlayerName);

                //message has successfully been received
                //ASCIIEncoding encoder = new ASCIIEncoding();
                //string msg = encoder.GetString(message, 0, bytesRead);
            }
        }


        public static void WriteTo(NetworkStream clientStream, object obj, string caller)
        {
            //first get the client name / player tag
            StreamWriter sr = new StreamWriter(clientStream);

            //byte[] aSize;
            Type objType = obj.GetType();

            bool isCollection;
            try
            {
                ICollection ic = (ICollection)obj;
                isCollection = true;
            }
            catch (Exception castException) {
                isCollection = false;
            }
            bool isHash = objType == typeof(Hashtable);
            if (obj == null)
            {
                sr.WriteLine(0);
                sr.Flush(); //flush stream
            }
            else if (isCollection && ((ICollection)obj).Count == 0)
            {
                sr.WriteLine(0);
                sr.Flush(); //flush stream
            }
            else if (isHash && ((Hashtable)obj).Count == 0)
            {
                sr.WriteLine(0);
                sr.Flush(); //flush stream
            }
            else
            {
                BinaryFormatter bf = new BinaryFormatter();
                //MemoryStream ms = new MemoryStream();
                //bf.Serialize(ms, obj);
                //byte[] ba = ms.GetBuffer();

                // C# to convert a byte array to a string.
                //string str;
                //System.Text.UTF8Encoding enc = new System.Text.UTF8Encoding();
                //str = enc.GetString(ba);

                //clientStream.Write(ba, 0, aSize.Length); //write the array
                sr.WriteLine("1");
                sr.Flush();
                try
                {
                    bf.Serialize(clientStream, obj);
                    sr.Flush(); //flush stream
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex.ToString());
                }
                //sr.WriteLine(str);
            }

            
        }
        // C# to convert a string to a byte array.
        public static byte[] StrToByteArray(string str)
        {
            System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding();
            return encoding.GetBytes(str);
        }


        public static object ReadFrom(NetworkStream clientStream, string caller)
        {
            //if (caller == "Server")
            //    Debug.Assert(false);

            StreamReader sr = new StreamReader(clientStream);
            //byte[] aSize = new byte[4];
            //clientStream.Read(aSize, 0, 4);

            string line = sr.ReadLine();
            int iSize = 0;
            try
            {
                if (line == null || line == "")
                    iSize = 0;
                else
                    iSize = int.Parse(line);
            }
            catch (Exception parseException) {}
            //int iSize = BitConverter.ToInt32(aSize, 0);

            if (iSize == 0)
            {
                return null;
            }
            else
            {
                BinaryFormatter bf = new BinaryFormatter();
                //string lineData = sr.ReadLine();
                //MemoryStream ms = new MemoryStream();
                //
                //byte[] ba = StrToByteArray(lineData);
                //ms.Write(ba, 0, ba.Length);
                //
                //ms.Position = 0;
                object obj;
                try
                {
                    obj = bf.Deserialize(clientStream);
                    bool deserializedOK = true;
                }
                catch (Exception parseExceptionOrEndOfStream) { 
                    obj = null;
                }
                return obj;
            }
            
        }

        const int msKindlyWait = 10;
        private static string waitingOnCanRead = null;
        private static string waitingOnCanWrite = null;
        public static void WaitForReadable(NetworkStream clientStream, string caller)
        {
            waitingOnCanRead = caller;
            while (true)
            {
                if (clientStream.CanRead ) //&& clientStream.DataAvailable) //should do max delay and also if dced
                    break;
                Thread.CurrentThread.Join(msKindlyWait);
            }
        }

        public static void WaitForWriteable(NetworkStream clientStream, string caller)
        {
            waitingOnCanWrite = caller;
            while (true)
            {
                if (clientStream.CanWrite) //should do max delay and also if dced
                    break;
                Thread.CurrentThread.Join(msKindlyWait);
            }
        }

    } //end class


    //http://www.dreamincode.net/forums/topic/24692-showing-the-external-ip-address-in-c%23/
    //Idea from: "How do i do to show my external IP-address in C# ?", but modified some.
    //Last accessed: Oct 15, 2010
	public class IpFinder
	{
		public static IPAddress GetExternalIp()
		{
			string whatIsMyIp = "http://www.whatismyip.com/automation/n09230945.asp";
			WebClient wc = new WebClient();
            
			UTF8Encoding utf8 = new UTF8Encoding();
			string sData = "";
			try
			{
                sData = wc.DownloadString(whatIsMyIp);
                //requestHtml = utf8.GetString(baTheData);
                IPAddress externalIp = IPAddress.Parse(sData);
                return externalIp;
			}
			catch (WebException we)
			{
				// do something with exception
				Debug.WriteLine(we.ToString());
                return null;
			}
			
		}
	}



} //end namespace
